﻿namespace Acl.Messages.Impl;

/// <summary>
/// 连接监视器
/// </summary>
internal class DistributionConnectionMonitor : BooleanDisposable, IDistributionConnectionMonitor
{
    private readonly CancellationTokenSource _cts;
    private readonly Task _timer;

    private readonly NatsConnection[] _conns;

    private DateTime? _offlineStartTime;
    private readonly int _pingInterval;
    private readonly double _reconnectInterval;

    /// <summary>
    ///
    /// </summary>
    /// <param name="serviceProvider"></param>
    public DistributionConnectionMonitor(IServiceProvider serviceProvider)
    {
        var connPool = serviceProvider.GetService<NatsConnectionPool>();
        if (connPool == null) _conns = [serviceProvider.GetRequiredService<NatsConnection>()];
        else _conns = connPool.GetConnections().OfType<NatsConnection>().ToArray();

        var options = serviceProvider.GetRequiredService<DistributionMessageBusOptions>();
        _reconnectInterval = options.ReconnectWaitMin.TotalSeconds;
        _pingInterval = (int)options.PingInterval.TotalSeconds;

        _cts = new CancellationTokenSource();
        _timer = Task.Run(LoopAsync);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _cts.Cancel();
        }
        base.Dispose(disposing);
    }

    public bool Connected { get; private set; }

    public event EventHandler<bool>? ConnectionStateChanged;

    private async Task LoopAsync()
    {
        while (!_cts.Token.IsCancellationRequested)
        {
            await Task.Delay(_pingInterval, _cts.Token).ConfigureAwait(false);

            var oldState = Connected;

            Connected = _conns.OfType<NatsConnection>().Any(x => x.ConnectionState == NatsConnectionState.Open);
            if (oldState != Connected)
            {
                try
                {
                    ConnectionStateChanged?.DynamicInvoke(this, Connected);
                }
                catch
                {
                }
            }

            if (_cts.Token.IsCancellationRequested)
                break;

            if (!Connected)
            {
                if (_offlineStartTime is null)
                {
                    _offlineStartTime = DateTime.Now;
                    await HeartCheck().ConfigureAwait(false);
                }
                else
                {
                    var interval = (DateTime.Now - _offlineStartTime.Value).TotalSeconds;
                    if (interval >= _reconnectInterval) //重连间隔检查
                    {
                        _offlineStartTime = DateTime.Now;
                        await HeartCheck().ConfigureAwait(false);
                    }
                }
            }
            else
            {
                _offlineStartTime = null;
            }
        }

     

        async Task HeartCheck()
        {
            foreach (var conn in _conns)
            {
                try
                {
                    await conn.PingAsync().ConfigureAwait(false);
                }
                catch { }
            }
        }
    }
}